Sending Information from One Screen to Another in Flutter
In Flutter, applications commonly contain multiple screens such as Home, Product Details, Profile, Settings, Cart, Checkout, Login, and Registration screens. When navigating from one screen to another, you often need to send information along with the navigation, such as a user ID, product object, name, email, selected item, or form data.
Flutter treats screens and pages as routes. The Navigator manages these routes, and Navigator.push() can be used to open a new route while passing data to the destination screen. :contentReference[oaicite:0]{index=0}
1. What Does Sending Information Between Screens Mean?
Sending information between screens means transferring a value or object from one Flutter screen to another while navigating.
For example:
- Home Screen sends a product to Product Details Screen.
- Login Screen sends a username to Dashboard Screen.
- Product List Screen sends a product ID to Product Details Screen.
- Profile Screen sends user information to Edit Profile Screen.
- Form Screen sends entered information to a Confirmation Screen.
Basic Flow
Screen A
|
| Data + Navigation
↓
Screen B
|
| Uses received data
↓
Display Information
2. Why Do We Need to Pass Data Between Screens?
Real-world applications rarely keep every piece of information on a single screen. Different screens perform different responsibilities, so data must often travel between them.
- To display details of a selected item.
- To pass a logged-in user's information.
- To pass IDs for database/API operations.
- To pass form values.
- To edit existing records.
- To pass configuration or screen parameters.
- To return a selected value to the previous screen.
3. Using Constructor Parameters
The simplest and most common approach is to pass data through the constructor of the destination widget.
Example: Sending a String
import 'package:flutter/material.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home')),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DetailsScreen(
message: 'Hello from Home Screen!',
),
),
);
},
child: const Text('Open Details'),
),
),
);
}
}
class DetailsScreen extends StatelessWidget {
final String message;
const DetailsScreen({
super.key,
required this.message,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Details')),
body: Center(
child: Text(message),
),
);
}
}
void main() {
runApp(
const MaterialApp(
home: HomeScreen(),
),
);
}
How This Works
- The Home Screen has a button.
- The button calls
Navigator.push().
DetailsScreen is created.
- The
message is passed through the constructor.
- The Details Screen displays the received message.
4. Passing Multiple Values
You can pass multiple values through a widget constructor.
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const ProfileScreen(
name: 'Rahul',
age: 25,
email: '[email protected]',
),
),
);
The destination screen can receive these values:
class ProfileScreen extends StatelessWidget {
final String name;
final int age;
final String email;
const ProfileScreen({
super.key,
required this.name,
required this.age,
required this.email,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Profile')),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Name: $name'),
Text('Age: $age'),
Text('Email: $email'),
],
),
),
);
}
}
5. Passing an Object Between Screens
For real applications, passing a complete Dart object is often more useful than passing many individual values. Flutter's official navigation cookbook demonstrates passing a custom object to a new screen. :contentReference[oaicite:1]{index=1}
Step 1: Create a Model Class
class Product {
final int id;
final String name;
final double price;
const Product({
required this.id,
required this.name,
required this.price,
});
}
Step 2: Create a Product
final product = Product(
id: 101,
name: 'Laptop',
price: 55000,
);
Step 3: Send the Object
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDetailsScreen(
product: product,
),
),
);
Step 4: Receive the Object
class ProductDetailsScreen extends StatelessWidget {
final Product product;
const ProductDetailsScreen({
super.key,
required this.product,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(product.name),
),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
Text('Product ID: ${product.id}'),
Text('Price: ₹${product.price}'),
],
),
),
);
}
}
6. Complete Product List to Product Details Example
This is one of the most common real-world patterns. A list screen displays products, and when the user taps a product, the selected product is sent to a details screen.
import 'package:flutter/material.dart';
class Product {
final int id;
final String name;
final double price;
const Product({
required this.id,
required this.name,
required this.price,
});
}
class ProductListScreen extends StatelessWidget {
ProductListScreen({super.key});
final List products = const [
Product(
id: 1,
name: 'Laptop',
price: 55000,
),
Product(
id: 2,
name: 'Smartphone',
price: 30000,
),
Product(
id: 3,
name: 'Headphones',
price: 3000,
),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Products'),
),
body: ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return ListTile(
title: Text(product.name),
subtitle: Text('₹${product.price}'),
trailing: const Icon(Icons.arrow_forward_ios),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ProductDetailsScreen(product: product),
),
);
},
);
},
),
);
}
}
class ProductDetailsScreen extends StatelessWidget {
final Product product;
const ProductDetailsScreen({
super.key,
required this.product,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(product.name),
),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
Text('Product ID: ${product.id}'),
const SizedBox(height: 10),
Text(
'₹${product.price}',
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
],
),
),
);
}
}
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
home: ProductListScreen(),
),
);
}
Flow
Product List
|
| User taps Laptop
↓
Product object
|
| Navigator.push()
↓
Product Details
|
↓
Display Laptop information
7. Sending Only an ID Between Screens
Sometimes you do not need to send an entire object. You can send an identifier such as a product ID or user ID.
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDetailsScreen(
productId: 101,
),
),
);
Receive it on the destination screen:
class ProductDetailsScreen extends StatelessWidget {
final int productId;
const ProductDetailsScreen({
super.key,
required this.productId,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Product Details'),
),
body: Center(
child: Text(
'Product ID: $productId',
),
),
);
}
}
This approach is useful when the destination screen will use the ID to fetch complete information from an API, database, or repository.
8. Passing Data Using RouteSettings
Flutter also supports passing information through RouteSettings.arguments. The destination screen can read the arguments using ModalRoute.of(context). :contentReference[oaicite:2]{index=2}
Sending Data
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DetailsScreen(),
settings: const RouteSettings(
arguments: 'Hello Flutter',
),
),
);
Receiving Data
class DetailsScreen extends StatelessWidget {
const DetailsScreen({super.key});
@override
Widget build(BuildContext context) {
final message =
ModalRoute.of(context)!.settings.arguments as String;
return Scaffold(
appBar: AppBar(
title: const Text('Details'),
),
body: Center(
child: Text(message),
),
);
}
}
9. Passing a Custom Object Using RouteSettings
class User {
final String name;
final String email;
const User({
required this.name,
required this.email,
});
}
Send the object:
final user = User(
name: 'Amit',
email: '[email protected]',
);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const UserScreen(),
settings: RouteSettings(
arguments: user,
),
),
);
Receive the object:
class UserScreen extends StatelessWidget {
const UserScreen({super.key});
@override
Widget build(BuildContext context) {
final user =
ModalRoute.of(context)!.settings.arguments as User;
return Scaffold(
appBar: AppBar(
title: const Text('User'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Name: ${user.name}'),
Text('Email: ${user.email}'),
],
),
);
}
}
10. Passing Data Using Named Routes
Flutter provides Navigator.pushNamed(), which can pass an object through the arguments parameter. Flutter's API documentation allows values such as strings, integers, maps, and custom objects to be passed this way. :contentReference[oaicite:3]{index=3}
However, Flutter's current documentation notes that named routes are no longer recommended for most new applications. They remain useful for learning, maintaining existing applications, and understanding older Flutter navigation patterns. :contentReference[oaicite:4]{index=4}
Example
Navigator.pushNamed(
context,
'/profile',
arguments: {
'name': 'Rahul',
'age': 25,
},
);
Reading the Data
class ProfileScreen extends StatelessWidget {
const ProfileScreen({super.key});
@override
Widget build(BuildContext context) {
final args =
ModalRoute.of(context)!.settings.arguments
as Map;
return Scaffold(
appBar: AppBar(
title: const Text('Profile'),
),
body: Column(
children: [
Text('Name: ${args['name']}'),
Text('Age: ${args['age']}'),
],
),
);
}
}
11. Passing Data with onGenerateRoute
onGenerateRoute can be used when route creation needs to inspect the route settings and construct the appropriate screen.
MaterialApp(
onGenerateRoute: (settings) {
if (settings.name == '/profile') {
final user =
settings.arguments as User;
return MaterialPageRoute(
builder: (context) => UserScreen(
user: user,
),
);
}
return null;
},
);
Navigate to the route:
Navigator.pushNamed(
context,
'/profile',
arguments: user,
);
12. Returning Information from the Second Screen
Data can also travel in the opposite direction. A second screen can return a result to the first screen using Navigator.pop(context, result). The first screen can wait for the result returned by Navigator.push(). Flutter's official cookbook demonstrates this pattern for returning a user's selection. :contentReference[oaicite:5]{index=5}
Second Screen
ElevatedButton(
onPressed: () {
Navigator.pop(context, 'Selected Item');
},
child: const Text('Select'),
)
First Screen
Future openSelectionScreen() async {
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SelectionScreen(),
),
);
if (!mounted) return;
if (result != null) {
print('Received: $result');
}
}
Data Flow
Screen A
|
| Send data
↓
Screen B
|
| Navigator.pop(context, result)
↓
Screen A
|
↓
Receive result
13. Sending Form Data to Another Screen
A common application requirement is to collect information on one screen and display it on another screen.
Form Screen
class FormScreen extends StatefulWidget {
const FormScreen({super.key});
@override
State createState() => _FormScreenState();
}
class _FormScreenState extends State {
final nameController = TextEditingController();
@override
void dispose() {
nameController.dispose();
super.dispose();
}
void submitForm() {
final name = nameController.text;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ResultScreen(
name: name,
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Form'),
),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
TextField(
controller: nameController,
decoration: const InputDecoration(
labelText: 'Enter Name',
),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: submitForm,
child: const Text('Submit'),
),
],
),
),
);
}
}
Result Screen
class ResultScreen extends StatelessWidget {
final String name;
const ResultScreen({
super.key,
required this.name,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Result'),
),
body: Center(
child: Text(
'Welcome, $name!',
style: const TextStyle(
fontSize: 24,
),
),
),
);
}
}
14. Sending Data from Login Screen to Dashboard
A typical login flow can pass basic user information to the dashboard.
class DashboardScreen extends StatelessWidget {
final String username;
const DashboardScreen({
super.key,
required this.username,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Dashboard'),
),
body: Center(
child: Text(
'Welcome $username',
),
),
);
}
}
Navigate after login:
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => DashboardScreen(
username: 'Manish',
),
),
);
pushReplacement() can be useful when the previous screen should be replaced rather than kept as the next screen in the navigation stack. Flutter documents it as one of the additional Navigator navigation methods. :contentReference[oaicite:6]{index=6}
15. Sending Data from ListView to Details Screen
This is one of the most important patterns for Flutter developers.
ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(products[index].name),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDetailsScreen(
product: products[index],
),
),
);
},
);
},
)
The selected item is available using products[index].
16. Sending Data Using a Map
A Map can be used when a small amount of loosely structured data needs to be passed.
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DetailsScreen(),
settings: RouteSettings(
arguments: {
'id': 101,
'name': 'Laptop',
'price': 55000,
},
),
),
);
Receive the Map:
final data =
ModalRoute.of(context)!.settings.arguments
as Map;
final id = data['id'];
final name = data['name'];
final price = data['price'];
When to Use a Model Instead
For larger applications, a dedicated Dart model class is generally easier to maintain than repeatedly accessing Map keys such as data['name'] and data['price'].
17. Sending Data Through a Selection Screen
Suppose a user needs to select a country.
Open Selection Screen
final selectedCountry = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const CountryScreen(),
),
);
Return Selected Country
ListTile(
title: const Text('India'),
onTap: () {
Navigator.pop(context, 'India');
},
)
Use Result
if (!mounted) return;
if (selectedCountry != null) {
setState(() {
country = selectedCountry;
});
}
18. Passing Data for Editing
Another common pattern is sending an existing object to an edit screen.
class User {
final String name;
final String email;
User({
required this.name,
required this.email,
});
}
Send the user:
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EditUserScreen(
user: user,
),
),
);
The edit screen can display the existing values inside TextEditingController objects and allow the user to modify them.
19. Passing Data Between Three Screens
Data can travel through multiple screens.
Home Screen
|
| Product
↓
Product Screen
|
| Product + Quantity
↓
Checkout Screen
|
| Order Data
↓
Confirmation Screen
Example
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CheckoutScreen(
product: product,
quantity: 2,
),
),
);
20. Passing Data Through a Custom Model
For complex applications, a model class makes the data structure clearer.
class Order {
final int orderId;
final String customerName;
final double amount;
final String status;
const Order({
required this.orderId,
required this.customerName,
required this.amount,
required this.status,
});
}
Send it:
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => OrderDetailsScreen(
order: order,
),
),
);
Receive it:
class OrderDetailsScreen extends StatelessWidget {
final Order order;
const OrderDetailsScreen({
super.key,
required this.order,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Order #${order.orderId}'),
),
body: Column(
children: [
Text(order.customerName),
Text('Amount: ₹${order.amount}'),
Text('Status: ${order.status}'),
],
),
);
}
}
21. Constructor vs RouteSettings
| Feature |
Constructor |
RouteSettings |
| Easy to understand |
Yes |
Moderate |
| Type safety |
Strong |
Requires casting |
| Good for custom objects |
Yes |
Yes |
| Direct dependency visible |
Yes |
No |
| Suitable for simple navigation |
Excellent |
Good |
22. Navigator.push() and Data Flow
Navigator.push() adds a route to the Navigator's route stack. It returns a Future, which can later complete with the result supplied when that route is popped. :contentReference[oaicite:7]{index=7}
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(
data: 'Hello',
),
),
);
Conceptually:
Navigator Stack
Before:
[Home]
After push:
[Home, SecondScreen]
23. Navigator.pop() and Returning Data
Navigator.pop() removes the current route. It can also provide a result to the route that originally pushed it. :contentReference[oaicite:8]{index=8}
Navigator.pop(context, 'Success');
The previous screen can receive the value:
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SecondScreen(),
),
);
print(result);
24. Type-Safe Data Passing
When possible, use typed constructor parameters or typed navigation results.
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SelectionScreen(),
),
);
This makes it clear that the screen is expected to return a String.
25. Handling Null Values
A returned value can be null if the user leaves the screen without returning a result.
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SelectionScreen(),
),
);
if (result != null) {
print('Selected: $result');
} else {
print('Nothing selected');
}
26. Handling Widget Lifecycle After await
When using await, the widget may no longer be mounted when execution resumes. If you need to call setState() or use the widget's context after the await, check mounted first.
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SelectionScreen(),
),
);
if (!mounted) return;
setState(() {
selectedValue = result;
});
27. Sending Data to an API-Driven Details Screen
A useful architecture is to send only the ID and let the destination screen retrieve the latest information.
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDetailsScreen(
productId: product.id,
),
),
);
Then the details screen can use the ID with a repository or API service.
Future loadProduct() async {
final product = await productRepository
.getProductById(productId);
if (!mounted) return;
setState(() {
currentProduct = product;
});
}
This pattern is useful when the destination screen should load current information instead of receiving a potentially outdated object.
28. Sending Information in an E-Commerce Application
Consider an e-commerce application:
Home
↓
Product List
↓
Product Details
↓
Cart
↓
Checkout
↓
Order Confirmation
Possible data transfers include:
| From |
To |
Data |
| Product List |
Product Details |
Product object |
| Product Details |
Cart |
Product + quantity |
| Cart |
Checkout |
Cart items + total |
| Checkout |
Confirmation |
Order object |
29. Common Mistakes
Mistake 1: Forgetting the Required Parameter
class DetailsScreen extends StatelessWidget {
final String name;
const DetailsScreen({
super.key,
required this.name,
});
}
When creating this widget, name must be supplied.
Mistake 2: Incorrect Type Casting
final data =
ModalRoute.of(context)!.settings.arguments as User;
Make sure the actual argument is a User object before casting it.
Mistake 3: Forgetting to Await a Result
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SelectionScreen(),
),
);
Mistake 4: Using a Map Everywhere
Maps can be convenient for small examples, but dedicated model classes make complex applications easier to maintain and refactor.
Mistake 5: Passing Too Much Unnecessary Data
If the destination only needs an ID, sending a large object may be unnecessary. Choose the smallest meaningful data required by the destination screen.
30. Best Practices
- Prefer constructor parameters for straightforward screen-to-screen data transfer.
- Use strongly typed model classes for complex data.
- Pass only the data the destination actually needs.
- Use IDs when the destination should load fresh data.
- Use
Navigator.pop() with a result when information needs to return to the previous screen.
- Check for
null when a navigation result is optional.
- After an asynchronous navigation result, check
mounted before updating widget state.
- Keep navigation logic easy to understand and test.
- For larger applications, use an appropriate routing/state-management architecture rather than passing large amounts of data through many screens.
- For new applications, be aware that Flutter's current documentation does not recommend named routes for most applications. :contentReference[oaicite:9]{index=9}
31. Real-World Example: Student Details
Student Model
class Student {
final String name;
final String course;
final String email;
const Student({
required this.name,
required this.course,
required this.email,
});
}
Student List
final students = [
const Student(
name: 'Amit',
course: 'Flutter',
email: '[email protected]',
),
const Student(
name: 'Neha',
course: 'Dart',
email: '[email protected]',
),
];
Navigate to Student Details
ListView.builder(
itemCount: students.length,
itemBuilder: (context, index) {
final student = students[index];
return ListTile(
title: Text(student.name),
subtitle: Text(student.course),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
StudentDetailsScreen(student: student),
),
);
},
);
},
)
Student Details Screen
class StudentDetailsScreen extends StatelessWidget {
final Student student;
const StudentDetailsScreen({
super.key,
required this.student,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(student.name),
),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Name: ${student.name}'),
Text('Course: ${student.course}'),
Text('Email: ${student.email}'),
],
),
),
);
}
}
32. Constructor-Based Data Passing vs Shared State
Not every piece of application data should be manually passed through every screen.
| Requirement |
Possible Approach |
| Small screen-specific value |
Constructor parameter |
| Selected object |
Constructor/model |
| Return selection |
Navigator.pop result |
| Legacy named navigation |
Route arguments |
| Application-wide state |
State-management solution |
| Server-side data |
Repository/API layer |
33. Important Flutter Navigation Methods
| Method |
Purpose |
Navigator.push() |
Open a new route. |
Navigator.pop() |
Close the current route and optionally return a result. |
Navigator.pushReplacement() |
Replace the current route. |
Navigator.pushAndRemoveUntil() |
Push a route and remove routes according to a condition. |
Navigator.popUntil() |
Pop routes until a condition is satisfied. |
Navigator.pushNamed() |
Navigate using a named route. |
Flutter's navigation documentation lists these and other Navigator methods for managing the route stack. :contentReference[oaicite:10]{index=10}
34. Quick Revision
- Flutter screens are represented as routes.
Navigator.push() opens another screen.
- Constructor parameters are a simple way to send information.
- Custom Dart objects can be passed between screens.
- An ID can be passed when the destination should load its own data.
RouteSettings.arguments can carry route arguments.
Navigator.pop(context, result) can return information.
await Navigator.push() can receive a typed result.
- Always handle nullable results when appropriate.
- Check
mounted after asynchronous operations before updating state.
- Named routes support arguments but are not recommended for most new Flutter applications according to current Flutter documentation.
35. Interview Questions
- How do you send data from one Flutter screen to another?
- What is the easiest way to pass data to a new screen?
- How can you pass a custom Dart object between screens?
- What is the purpose of
Navigator.push()?
- What is the purpose of
Navigator.pop()?
- How can a screen return data to the previous screen?
- What is
RouteSettings.arguments?
- How can you receive arguments using
ModalRoute.of(context)?
- How can you pass data using
Navigator.pushNamed()?
- Why might a model class be preferable to a Map?
- When should you pass an ID instead of a complete object?
- Why should you check
mounted after awaiting navigation?
- What is the difference between
push() and pushReplacement()?
- What happens to the Navigator stack when
push() is called?
- Why are named routes not recommended for most new Flutter applications?
36. Practical Exercise
Create a Flutter application with the following flow:
Home Screen
↓
Student List
↓
Student Details
↓
Edit Student
↓
Return Updated Student
↓
Student List
Requirements
- Create a
Student model.
- Create a list containing at least five students.
- Display students using
ListView.builder.
- Pass the selected student to a details screen.
- Display the student's name, email, and course.
- Add an Edit button.
- Pass the student to the edit screen.
- Allow the user to modify the student's name.
- Return the updated name using
Navigator.pop().
- Update the original screen after receiving the result.
37. Key Takeaways
Sending information from one screen to another is a fundamental Flutter development skill. For simple navigation, constructor parameters provide a clear and strongly typed approach. For more complex flows, custom model objects, IDs, route arguments, and returned navigation results can be used according to the application's requirements.
The most important pattern to remember is:
// Send
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailsScreen(
data: value,
),
),
);
// Receive
class DetailsScreen extends StatelessWidget {
final String data;
const DetailsScreen({
super.key,
required this.data,
});
@override
Widget build(BuildContext context) {
return Text(data);
}
}
38. Official Flutter Resources
39. Flutter Training Resources
For structured Flutter learning and course information, visit: